package com.lt.flashcard.webservice.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanWrapper;

/**
 * @author ChauDinh
 * 
 */
public class ObjectUtils {

    private static final Logger LOGGER = Logger.getLogger(ObjectUtils.class);

    /**
     * Private constructor to avoid instantiation.
     */
    private ObjectUtils() {
        // do something
    }

    /**
     * Deeply copy an object.
     * 
     * @param source
     *            Source object.
     * @return Deep copy of the <code>source</code> object.
     */
    public static Serializable copy(Serializable source) {
        Serializable result = null;
        try {
            byte[] b = serialize(source);
            result = unserialize(b);
        } catch (Exception e) {
            LOGGER.error("Error in copying object", e);
            throw new RuntimeException(e);
        }
        return result;
    }

    private static byte[] serialize(Serializable object) throws IOException {
        ByteArrayOutputStream stream = null;
        ObjectOutputStream out = null;
        try {
            stream = new ByteArrayOutputStream();
            out = new ObjectOutputStream(stream);
            out.writeObject(object);
            out.flush();
            return stream.toByteArray();
        } finally {
            if (stream != null) {
                stream.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    private static Serializable unserialize(byte[] array) throws IOException, ClassNotFoundException {
        ByteArrayInputStream stream = null;
        ObjectInputStream in = null;
        try {
            stream = new ByteArrayInputStream(array);
            in = new ObjectInputStream(stream);
            return (Serializable) in.readObject();
        } finally {
            if (stream != null) {
                stream.close();
            }
            if (in != null) {
                in.close();
            }
        }
    }

    /**
     * @author ChauDinh
     * @param obj
     * @param property
     * @return object
     */
    public static Object extractPropertyValue(Object obj, String property) {
        return property == null ? null : new CustomizedBeanWrapperImpl(obj).getPropertyValue(property);
    }

    /**
     * Extract properties from given object by a provided array of object attributes. (Item in properties array must be
     * attribute name of object).
     * 
     * @param obj
     *            The object need to be extracted
     * @param properties
     *            Array of object attributes to get value from object
     * @return A map contain object value along with corresponding key (attribute name).
     */
    public static Map<String, Object> extractPropertyValues(Object obj, String[] properties) {
        if (properties == null || properties.length == 0) {
            return null;
        }
        BeanWrapper bw = new CustomizedBeanWrapperImpl(obj);
        Map<String, Object> result = new HashMap<String, Object>();
        for (int i = 0; i < properties.length; i++) {
            result.put(properties[i], bw.getPropertyValue(properties[i]));
        }
        return result;
    }

    /**
     * Extract an object list to a map list (with given attributes to retrieve value).
     * 
     * @param objects
     *            List of objects to be extracted
     * @param properties
     *            Array of object attributes to get value from object
     * @return A list which contain attribute maps.
     */
    @SuppressWarnings("rawtypes")
    public static List<Map<String, Object>> extractPropertyValuesAsList(List objects, String[] properties) {
        if (objects == null || objects.size() == 0) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(objects.size());
        for (Object object : objects) {
            result.add(extractPropertyValues(object, properties));
        }
        return result;
    }

}